package com.cn.serverline.lineicp.productionLine.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cn.serverline.lineicp.productionLine.entity.ProcessMaterials;
import com.cn.serverline.lineicp.productionLine.entity.ProcessSteps;
import com.cn.serverline.lineicp.productionLine.entity.ProductMix;
import com.cn.serverline.lineicp.productionLine.entity.ProductionProcesses;
import com.cn.serverline.lineicp.productionLine.enums.ProductionProcessesException;
import com.cn.serverline.lineicp.productionLine.enums.WorkerTechnicalLevelEnum;
import com.cn.serverline.lineicp.productionLine.mapper.ProcessMaterialsMapper;
import com.cn.serverline.lineicp.productionLine.mapper.ProductMixMapper;
import com.cn.serverline.lineicp.productionLine.mapper.ProductionProcessesMapper;
import com.cn.serverline.lineicp.productionLine.param.*;
import com.cn.serverline.lineicp.productionLine.service.IProcessStepsService;
import com.cn.serverline.lineicp.productionLine.service.IProductionProcessesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import cn.stylefeng.roses.kernel.db.api.pojo.page.PageResult;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.db.api.factory.PageResultFactory;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cn.stylefeng.roses.kernel.db.api.factory.PageFactory;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;


/**
 * <p>
 * 工序表 服务实现类
 * </p>
 *
 * @author Gmr
 * @since 2024-07-30
 */
@Service("ProductionProcessesServiceImpl")
public class ProductionProcessesServiceImpl extends ServiceImpl<ProductionProcessesMapper, ProductionProcesses> implements IProductionProcessesService {

    // 产品结构Mapper
    @Resource
    private ProductMixMapper productMixMapper;

    // 工序工步服务
    @Resource(name = "ProcessStepsServiceImpl")
    private IProcessStepsService iProcessStepsService;

    // 工序物料Mapper
    @Resource
    private ProcessMaterialsMapper processMaterialsMapper;

    /**
     * 新增
     * @param processesAddParam 请求参数
     */
    @Override
    public void add(ProductionProcessesAddParam processesAddParam) {

        // 校验上级零组件是否存在
        getProductMix(processesAddParam.getProductMixId());

        // 校验工序代号是否重复
        if(checkProcessCodeDuplicate(processesAddParam.getProcessCode(), processesAddParam.getProductMixId(), null)) {
            throw new ServiceException(ProductionProcessesException.PRODUCTION_PROCESSES_CODE_DUPLICATE);
        }

        // 校验工人技术等级是否存在
        WorkerTechnicalLevelEnum.validate(processesAddParam.getWorkerSkillLevel());

        ProductionProcesses productionProcesses = BeanUtil.copyProperties(processesAddParam, ProductionProcesses.class);

        // 获取上一条数据
        List<ProductionProcesses> processes = this.list(Wrappers.<ProductionProcesses>lambdaQuery()
                .orderByDesc(ProductionProcesses::getCreateTime));
        // 设置排序
        productionProcesses.setSort(CollectionUtil.isEmpty(processes) ? 0 : processes.get(0).getSort() + 1);
        this.save(productionProcesses);
    }

    /**
     * 删除
     * @param idParam 请求参数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void del(ProductionProcessesIdParam idParam) {
        // 校验工序是否存在
        getInfo(idParam.getId());

        // 获取工序下的工步
        List<ProcessSteps> processSteps = iProcessStepsService.getBaseMapper().selectList(Wrappers.<ProcessSteps>lambdaQuery()
                .eq(ProcessSteps::getProductionProcessesId, idParam.getId()));

        // 如果不为空删除工步
        if(CollectionUtil.isNotEmpty(processSteps)) {
            iProcessStepsService.del(processSteps.stream()
                    .map(ProcessSteps::getId)
                    .map(item -> {
                        ProcessStepsIdParam processStepsIdParam = new ProcessStepsIdParam();
                        processStepsIdParam.setId(item);
                        return processStepsIdParam;
                    })
                    .collect(Collectors.toList()));
        }

        // 获取工序下的物料
        List<ProcessMaterials> processMaterials = processMaterialsMapper.selectList(Wrappers.<ProcessMaterials>lambdaQuery()
                .eq(ProcessMaterials::getProductionProcessesId, idParam.getId()));

        // 如果不为空删除物料
        if(CollectionUtil.isNotEmpty(processMaterials)) {
            processMaterialsMapper.deleteBatchIds(processMaterials.stream()
                    .map(ProcessMaterials::getId)
                    .collect(Collectors.toList()));
        }

        this.removeById(idParam.getId());
    }

    /**
     * 修改
     * @param processesEditParam 请求参数
     */
    @Override
    public void edit(ProductionProcessesEditParam processesEditParam) {

        // 校验工序是否存在，并获取工序信息
        ProductionProcesses processes = getInfo(processesEditParam.getId());

        // 校验工序代号是否重复
        if(checkProcessCodeDuplicate(processesEditParam.getProcessCode(), processes.getProductMixId(), processes.getId())) {
            throw new ServiceException(ProductionProcessesException.PRODUCTION_PROCESSES_CODE_DUPLICATE);
        }

        // 检验工人等级是否存在
        WorkerTechnicalLevelEnum.validate(processesEditParam.getWorkerSkillLevel());

        // 构建工序信息
        BeanUtil.copyProperties(processesEditParam, processes);
        this.updateById(processes);
    }

    /**
     * 查询详情
     * @param idParam 请求参数
     */
    @Override
    public ProductionProcesses detail(ProductionProcessesIdParam idParam) {
        return getInfo(idParam.getId());
    }

    /**
     * 分页查询
     * @param pageParam 请求参数
     */
    @Override
    public PageResult<ProductionProcesses> findPage(ProductionProcessesPageParam pageParam) {
        LambdaQueryWrapper<ProductionProcesses> wrapper = new LambdaQueryWrapper<>();
        // 筛选主要属性
        wrapper.select(ProductionProcesses::getId, ProductionProcesses::getProcessName, ProductionProcesses::getProcessCode,
                        ProductionProcesses::getFactoryCode,ProductionProcesses::getUnitTime,ProductionProcesses::getWorkerSkillLevel,
                        ProductionProcesses::getSort, ProductionProcesses::getCreateTime)
                .eq(ProductionProcesses::getProductMixId, pageParam.getProductMixId())
                .orderByAsc(ProductionProcesses::getSort);

        Page<ProductionProcesses> sysRolePage = this.page(PageFactory.defaultPage(), wrapper);
        return PageResultFactory.createPageResult(sysRolePage);
    }

    /**
     * 校验工序是否存在
     * @param id 工序ID
     * @return 工序信息
     */
    private ProductionProcesses getInfo(Long id) {
        // 判断id是否存在
        if(ObjectUtil.isEmpty(id)) {
            throw new ServiceException(ProductionProcessesException.PRODUCTION_PROCESSES_ID_NOT_EXIST);
        }
        ProductionProcesses processes = this.getById(id);

        // 判断对象是否存在
        if(ObjectUtil.isEmpty(processes)) {
            throw new ServiceException(ProductionProcessesException.PRODUCTION_PROCESSES_NOT_EXIST);
        }
        return processes;
    }

    /**
     * 根据零组件ID获取零组件
     * @param productMixId 零组件ID
     * @return 零组件
     */
    private ProductMix getProductMix(Long productMixId) {
        ProductMix productMix = productMixMapper.selectById(productMixId);
        if(ObjectUtil.isEmpty(productMix)) {
            throw new ServiceException(ProductionProcessesException.SPARE_PARTS_NOT_EXIST);
        }
        return productMix;
    }

    /**
     * 检查工序代号是否重复
     * @param processCode 工序代号
     * @param parentId 父级ID
     * @param id id
     * @return 是否重复
     */
    private boolean checkProcessCodeDuplicate(String processCode,Long parentId, Long id) {
        return this.count(Wrappers.<ProductionProcesses>lambdaQuery()
                .eq(ProductionProcesses::getProductMixId, parentId)
                .eq(ProductionProcesses::getProcessCode, processCode)
                .ne(ObjectUtil.isNotEmpty(id), ProductionProcesses::getId, id)) > 0;
    }
}
